-- icache wrapper
-- this is provided for a place holder until you do the cache labs
-- until then you should just place this file between your fetch stage
-- of your pipeline and your priority mux for main memory.
library ieee;
use ieee.std_logic_1164.all;

entity icache is
  port(
    CLK       : in  std_logic;
    nReset    : in  std_logic;

    iMemRead  : in  std_logic;                       -- CPU side
    iMemWait  : out std_logic;                       -- CPU side
    iMemAddr  : in  std_logic_vector (15 downto 0);  -- CPU side
    iMemData  : out std_logic_vector (31 downto 0);  -- CPU side

    aiMemWait : in  std_logic;                       -- arbitrator side
    aiMemRead : out std_logic;                       -- arbitrator side
    aiMemAddr : out std_logic_vector (15 downto 0);  -- arbitrator side
    aiMemData : in  std_logic_vector (31 downto 0)   -- arbitrator side
    );

end icache;

architecture struct of icache is
  
  component iram16x32 is
    port
    (
      clk				:	IN	STD_LOGIC;
      nReset : IN STD_LOGIC;
      addr      : IN  STD_LOGIC_VECTOR (3 DOWNTO 0);
      we        : IN  STD_LOGIC := '1';
      writeport : IN  STD_LOGIC_VECTOR (42 DOWNTO 0);
      readport  : OUT STD_LOGIC_VECTOR (42 DOWNTO 0)
    );
  end component;

  subtype framedata is std_logic_vector(42 downto 0);
  --   v:0 tag:0000000000     Data:00000000000000000000000000000000
  --  Index:0000  BOffset:00
  signal  c_rdata, c_wdata : framedata;
  signal  c_we  : std_logic;
  
  signal  valid : std_logic;
  signal  tag   : std_logic_vector(9 downto 0);
  signal  data  : std_logic_vector(31 downto 0);
  
  signal hit  : std_logic;
  
  
  signal index  : std_logic_vector(3 downto 0);
  type states is (IDLE, READ);
  signal state, nextstate : states;
  
begin

  valid <= c_rdata(42);
  tag   <= c_rdata(41 downto 32);
  data  <= c_rdata(31 downto 0);
  
  index <= iMemAddr(5 downto 2);


  c_wdata <= '1' & iMemAddr(15 downto 6) & aiMemData;
  
  
  
  I : iram16x32 
    port map (
      clk				     => CLK,
      nReset      => nReset,
      addr        => index,
      we          => c_we,
      writeport   => c_wdata,
      readport    => c_rdata
    );
    
    hit <= '1' when tag = iMemAddr(15 downto 6) and valid = '1' else
            '0';

	-- pass the values through, this block does nothing.
	-- you may use it when you do the cache labs.
	
	statereg : process(CLK, nReset)
  begin
    if nReset = '0' then
      state <= IDLE;
    elsif rising_edge(CLK) then
      state <= nextstate;
    end if;
  end process statereg;
  
  next_state : process(state, nextstate, hit, aiMemWait, iMemRead)
    begin
        c_we <= '0';
        nextstate <= IDLE;
        
      case state is
        
      when IDLE =>
        nextstate <= IDLE;
        aiMemRead <= '0';
        iMemWait <= '0';
        
        if (hit = '0' and iMemRead = '1' and aiMemWait = '0') then
          nextstate <= READ;
          iMemWait <= '1';
          --aiMemRead <= '1';
        elsif (hit = '0' and iMemRead = '1' and aiMemWait = '1') then
          nextstate <= IDLE;
          iMemWait <= '1';
        end if;

      when READ =>
        iMemWait <= '1';
        aiMemRead <= '1'; 
        
        if(aiMemWait = '1') then
          nextstate <= READ;
        elsif (aiMemWait = '0') then
          c_we <= '1';
          nextstate <= IDLE;
        end if;
        
      end case;
  end process next_state;

--iMemWait <= not hit; --tells processor to wait
iMemData <= data;

aiMemAddr <= iMemAddr;


end struct;
